<?php

namespace fast;

/**
 * 全境供应链云-工具类
 * 文档地址:https://sce-opc.380star.com/#/passport/api-service
 */
class SceApiService
{
    //appkey
    private $appKey = '1861258982681595906';

    //appSecret 秘钥
    private $appSecret = '0fc63adad112db8c9e27b4ac6cbd3b31';

    //当前时间毫秒数
    private $timeStamp = '';

    //随机生成数字
    private $randomNumber = '';

    //版本号,默认为1.0
    private $version = '1.0';

    //请求头参数
    private $header = [];

    //API接口列表
    private const APIS = [
        //查询消息池中的消息
        'getMessage'=>'https://sce-opz.changecloud.cn/scce/opm/opm/messagePool/list/page',
        /**********************************商品***********************************/
        //获取省市区地址
        'getRegionByCodeOpen'=>'https://sce-opz.changecloud.cn/scce/pbc/pbc/region/getRegionByCodeOpen',
        //获取品牌,POST
        'getBrands' => 'https://sce-opz.changecloud.cn/scce/cmc/cmc/spu/open/getBrands',
        //获取分类,POST
        'getCategorys' => 'https://sce-opz.changecloud.cn/scce/cmc/cmc/spu/open/getCategorys',
        //查询商品库SPUID列表,POST
        'getSpuIdList' => 'https://sce-opz.changecloud.cn/scce/cmc/cmc/spu/open/getSpuIdList',
        //查询SPU商品详情,POST
        'getSpuBySpuIds' => 'https://sce-opz.changecloud.cn/scce/cmc/cmc/spu/open/getSpuBySpuIds',
        //查询SKU规格信息,POST
        'listSkuBySpuId' => 'https://sce-opz.changecloud.cn/scce/cmc/cmc/spu/open/listSkuBySpuId',
        //查询商品运费,POST
        'calculate' => 'https://sce-opz.changecloud.cn/scce/cmc/cmc/freight/calculate',
        //查询商品价格,POST
        'findSkuSalePrice' => 'https://sce-opz.changecloud.cn/scce/cmc/cmc/spu/open/goods/findSkuSalePrice',
        //查询商品库存,POST
        'findSkuStock' => 'https://sce-opz.changecloud.cn/scce/cmc/cmc/spu/open/findSkuStock',

        /**********************************订单***********************************/
        //批量校验下单商品销售范围和库存,POST
        'checkSkuStock'=>'https://sce-opz.changecloud.cn/scce/cmc/cmc/spu/open/checkSkuStock',
        //创建订单,POST
        'submitOrder' => 'https://sce-opz.changecloud.cn/scce/ctc/ctc/reseller/order/submitOrder',
        //修改订单收货地址,POST
        'updateOrderReceiveAddress' => 'https://sce-opz.changecloud.cn/scce/ctc/ctc/reseller/order/updateOrderReceiveAddress',
        //查询订单详情,POST
        'getOrderDetail' => 'https://sce-opz.changecloud.cn/scce/ctc/ctc/reseller/order/getOrderDetail',
        //查询物流信息,POST
        'getOrderExpressListByOs' => 'https://sce-opz.changecloud.cn/scce/ctc/ctc/tOrderExpress/getOrderExpressListByOs',
        //确认收货,POST
        'confirmReceive' => 'https://sce-opz.changecloud.cn/scce/ctc/ctc/reseller/order/confirmReceive',
        //售后申请,POST
        'applyRefund' => 'https://sce-opz.changecloud.cn/scce/ctc/ctc/reseller/orderReturnGoods/applyRefund',
        //退货/换货邮寄卖家,POST
        'sendReturnGoods' => 'https://sce-opz.changecloud.cn/scce/ctc/ctc/reseller/orderReturnGoods/sendReturnGoods',
        //换货确认收货,POST
        'receiptRefund' => 'https://sce-opz.changecloud.cn/scce/ctc/ctc/reseller/orderReturnGoods/receiptRefund',
        //取消售后申请,POST
        'cancelRefund' => 'https://sce-opz.changecloud.cn/scce/ctc/ctc/reseller/orderReturnGoods/cancelRefund',
        //查询售后单详细信息,POST
        'detail' => 'https://sce-opz.changecloud.cn/scce/ctc/ctc/reseller/orderReturnGoods/detail'
    ];

    public function __construct()
    {

        $this->header = [
            'Content-Type:application/json;charset=utf-8',
            'appKey:'.$this->appKey,
            'version:'.$this->version
        ];
    }


    /**
     * 查询消息池中的消息
     * @param string $enumNum 消息枚举值：1-订单状态变更,2-订单发货，3-售后订单状态变更，4-商品信息、状态变更通知,5-商品删除通知,6-商品规格删除通,7-商品规格变更通知,8-选品库商品删除通知,9-选品库商品添加通知,10-添加选品库商品通知,11-删除选品库商品通知,12-选品库商品价格变更通知，20-创建订单
     * @return array
     */
    public function getMessage(string $enumNum='5')
    {
        $params = [
            'enumNum' => $enumNum,//区域编码	string	是否必填:false
        ];
        $resp =$this->postRequest(self::APIS['getMessage'], $params, $this->header);
        return $resp;
    }

    /**
     * 获取省市区地址
     * @param string $regionCode 区域编码
     * @return array
     */
    public function getRegionByCodeOpen(string $regionCode='')
    {
        /*$params = [
            'regionCode' => $regionCode,//区域编码	string	是否必填:false
        ];*/
        $params=$regionCode?['regionCode' => $regionCode]:[];
        $resp =$this->postRequest(self::APIS['getRegionByCodeOpen'], $params, $this->header);
        return $resp;
    }


    /**
     * 获取品牌
     * @param int $brandId 品牌ID
     * @param int $pageIndex 当前页索引
     * @param int $pageSize 每页显示条数
     * @return void
     */
    public function getBrands($pageIndex = 0, $pageSize = 10)
    {
        $params = [
            'pageIndex' => $pageIndex,//当前页索引,是否必填:true
            'pageSize' => $pageSize,//每页显示条数,是否必填:true
            //'brandId'=>0,//品牌ID,是否必填:false
            //'sort'=>'' //排序字段(支持多字段，传入数据格式：name.ascend-age.descend),是否必填:false
        ];
        $resp =$this->postRequest(self::APIS['getBrands'], $params, $this->header);
        return $resp;
    }


    /**
     * 获取分类
     * @param int $pid 上级类目ID,一级类目的上级为0
     * @return void
     */
    public function getCategorys(int $pid)
    {
        $params = [
            'id'=>$pid,//上级类目ID,一级类目的上级为0,是否必填:false
        ];
        $resp =$this->postRequest(self::APIS['getCategorys'],$params,$this->header);
        return $resp;
    }

    /***************************************************商品*******************************************/

    /**
     * 查询商品库SPUID列表
     * @param string $firstCategoryId 一级分类ID
     * @return void
     */
    public function getSpuIdList(int $pageIndex=1)
    {
        $params = [
            //'hasVideo'=>'',//	商品是否含视频： -1、全部 1、只含视频，默认 -1	string	是否必填:false
            //'createStartTime'=>'',//	选品开始时间，格式 为 'yyyy-MM-dd HH:mm:ss'	string	是否必填:false
            'pageIndex'=>$pageIndex,//	页码，默认 1	string	是否必填:false
            //'createEndTime'=>'',//	选品截止时间，格式 为 'yyyy-MM-dd HH:mm:ss'	string	是否必填:false
            //'brandId'=>'',//	品牌ID	string	是否必填:false
            //'pageSize'=>$pageSize,//	每页大小，默认 500，最大1000	string	是否必填:false
            //'firstCategoryId'=>'',//	一级分类ID	string	是否必填:false
            //'secondCategoryId'=>'',//	二级分类ID	string	是否必填:false
            //'thirdCategoryId'=>'',//	三级分类ID	string	是否必填:false
            'status'=>1,//	商品状态： -1.全部，1.已上架，2.已下架，3.已失效，默认 1	string	是否必填:false
        ];
        $resp =$this->postRequest(self::APIS['getSpuIdList'], $params, $this->header);
        return $resp;
    }


    /**
     * 查询SPU商品详情
     * @param array $spuIdList 商品ID数组
     * @return void
     */
    public function getSpuBySpuIds(array $spuIdList = [])
    {
        $params = [
            'spuIdList' => $spuIdList //spuIdList	array	是否必填:true
        ];
        $resp =$this->postRequest(self::APIS['getSpuBySpuIds'], $params, $this->header);
        return $resp;
    }


    /**
     * 查询SKU规格信息
     * @param int $spuId spuId
     * @return void
     */
    public function listSkuBySpuId(int $spuId =0)
    {
        $params = [
            'spuId' => $spuId //spuId	integer	是否必填:true
        ];
        $resp =$this->postRequest(self::APIS['listSkuBySpuId'], $params, $this->header);
        return $resp;
    }


    /**
     * 查询商品运费
     * @param array $spuInfoList 订单商品及数量,包含quantity(商品数量,integer,是否必填:true),spuId(商品spuId,integer,是否必填:true)
     * @param string $cityId 收货城市ID（二级区域编码)
     * @return mixed
     */
    public function calculate(array $spuInfoList = [], $cityId = '000000')
    {
        $params = [
            /*'spuInfoList' => [//订单商品及数量	array	是否必填:true
                'quantity' => 0,//商品数量	integer	是否必填:true
                'spuId' => 0,//商品spuId	integer	是否必填:true
            ],*/
            'spuInfoList'=>[$spuInfoList],
            'cityId' =>$cityId    //收货城市ID（二级区域编码)	string	是否必填:true
        ];
        $resp =$this->postRequest(self::APIS['calculate'], $params, $this->header);
        return $resp;
    }


    /**
     * 查询商品价格
     * @param string $skuIds 商品ID，最多查200个，多个以英文逗号拼接
     * @return void
     */
    public function findSkuSalePrice(string $skuIds = '')
    {
        $params = [
            'skuIds' => $skuIds //skuIds 商品ID，最多查200个，多个以英文逗号拼接 string	是否必填:true
        ];
        $resp =$this->postRequest(self::APIS['findSkuSalePrice'], $params, $this->header);
        return $resp;
    }


    /**
     * 查询商品库存
     * @return void
     */
    public function findSkuStock()
    {
        $params = [
            //'distriId'=>'',//分销商ID	integer	是否必填:false
            //'skuIdList'=>[]//skuIdList	array	是否必填:false
        ];
        $resp =$this->postRequest(self::APIS['findSkuStock'], $params, $this->header);
        return $resp;
    }



    /***************************************************订单*******************************************/

    /**
     * 批量校验下单商品销售范围和库存
     * @param array $itemDTOList sku库存数组,包含skuId(skuId,integer,是否必填:true)和skuNum(sku数量,integer,是否必填:true)
     * @param string $areaRegionCode 地区编码，四级区域编码，实在没有传三级区域编码	string	是否必填:true
     */
    public function checkSkuStock(array $itemDTOList,string $areaRegionCode='')
    {
        $params = [
             /*'itemDTOList'=>[//sku库存组合	array	是否必填:true
               'skuId'=>0,//	skuId	integer	是否必填:true
               'skuNum'=>0//	sku数量	integer	是否必填:true
             ],*/
            'itemDTOList'=>[$itemDTOList],
            'areaRegionCode'=>$areaRegionCode //地区编码，四级区域编码，实在没有传三级区域编码	string	是否必填:true
        ];
        $resp =$this->postRequest(self::APIS['checkSkuStock'], $params, $this->header);
        return $resp;
    }
    /**
     * 创建订单
     * @param array $goodsList 商品数组,包含goodsQty(商品数,string,必填)和skuId(SKUID,string,必填)
     * @param string $shipName 收货联系人姓名
     * @param string $shipAddress 收货地址
     * @param string $shipMobile 收货手机
     * @param string $shipAreaCode 收货地区编码
     * @param string $outOrderSn 外部订单号(分销商订单号)
     * @param string $buyerMemo  订单备注
     * @return array 响应结果
     */
    public function submitOrder(array $goodsList=[],string $shipName='',string $shipAddress='',string $shipMobile='',string $shipAreaCode='',string $outOrderSn='',string $buyerMemo='')
    {
        $params = [
            'orderSource' =>1,//	订单来源：1 开放平台	integer	是否必填:true
            /*'goodsList' => [//	商品列表	array	是否必填:true
                [
                'goodsQty' => '',    //商品数	string	是否必填:true
                'skuId' => '',    //SKUID	string	是否必填:true
                ]
            ],*/
            'goodsList' =>$goodsList,
            //'buyerName'=>'',//	下单用户名称	string	是否必填:false
            'shipName' =>$shipName,//	收货联系人姓名	string	是否必填:true
            'shipAddress' =>$shipAddress,//收货地址，如创业路东	string	是否必填:true
            //'shipPhone'=>'',//	收货电话，可选	string	是否必填:false
            'shipMobile' =>$shipMobile,//	收货手机	string	是否必填:true
            //'taxPayer'=>'',//	纳税人识别号	string	是否必填:false
            'outOrderSn'=>$outOrderSn,//	外部订单号	string	是否必填:false
            //'invoiceType'=>'',//	发票类型（1;个人，2;公司，0;不开发票,默认值0）	string	是否必填:false
            //'invoice'=>'',//	发票抬头	string	是否必填:false
            //'shipZipCode'=>'',//	收货邮编	string	是否必填:false
            //'buyerMemo'=>'',//	订单备注	string	是否必填:false
            'shipAreaCode' =>$shipAreaCode,//	收货地区编码，例如:省ID,市ID,区ID,镇ID（三级与四级地址均可下单）	string	是否必填:true
        ];
        if($buyerMemo)$params['buyerMemo'] = $buyerMemo;
        $resp =$this->postRequest(self::APIS['submitOrder'], $params, $this->header);
        return $resp;
    }


    /**
     * 修改订单收货地址
     * @param string $shipMobile 收货手机
     * @param string $orderSn 订单编号
     * @param string $shipName 收货联系人姓名
     * @param string $shipAddress 收货地址
     * @param string $shipAreaCode 收货地区编码
     */
    public function updateOrderReceiveAddress(string $shipMobile='',string $orderSn='',string $shipName='',string $shipAddress='',string $shipAreaCode='')
    {
        $params = [
            'shipMobile' =>$shipMobile,//	收货手机	string	是否必填:true
            'orderSn' =>$orderSn,//	订单编号	string	是否必填:true
            'shipName' =>$shipName,//	收货联系人姓名	string	是否必填:true
            //'shipZipCode'=>'',//	收货邮编	string	是否必填:false
            'shipAddress' =>$shipAddress,//	收货地址，如创业路东	string	是否必填:true
            'shipAreaCode' =>$shipAreaCode,//	收货地区编码，例如:省ID,市ID,区ID,镇ID（三级与四级地址均可）	string	是否必填:true
        ];
        $resp =$this->postRequest(self::APIS['updateOrderReceiveAddress'], $params, $this->header);
        return $resp;
    }


    /**
     * 查询订单详情
     * @param string $cloudOrderSn 云端订单编号	string	是否必填:true
     * @return mixed
     */
    public function getOrderDetail(string $cloudOrderSn='')
    {
        $params = [
            'orderSn' =>$cloudOrderSn,//订单编号 string	是否必填:true
        ];
        $resp =$this->postRequest(self::APIS['getOrderDetail'], $params, $this->header);
        return $resp;
    }


    /**
     * 查询物流信息
     * @param string $deliverySn 物流单号 string 是否必填:true
     * @param string $orderSn 订单编号（如果是售后单，传售后单号）	string 是否必填:true
     * @return mixed
     */
    public function getOrderExpressListByOs(string $deliverySn='',string $orderSn='')
    {
        $params = [
            'deliverySn' =>$deliverySn,//物流单号	string	是否必填:true
            'orderSn' =>$orderSn,//订单编号（如果是售后单，传售后单号）	string	是否必填:true
        ];
        $resp =$this->postRequest(self::APIS['getOrderExpressListByOs'], $params, $this->header);
        return $resp;
    }


    /**
     * 确认收货
     * @param string $orderSn 云端订单编号
     * @param string $operator 操作人
     * @return mixed
     */
    public function confirmReceive(string $orderSn='',string $operator='')
    {
        $params = [
            'orderSn' =>$orderSn,//订单编号	string	是否必填:true
            'operator' =>$operator,//操作人	string	是否必填:true
        ];
        $resp =$this->postRequest(self::APIS['confirmReceive'], $params, $this->header);
        return $resp;
    }


    /**
     * 售后申请
     * @param string $reason 退费原因
     * @param string $orderSn 订单编号
     * @param string $refundReasonId 退款原因ID, 请参考文档中心-订单售后申请原因说明文档
     * @param array $goodsList 退款商品数组,包含goodsQuantity(商品数,integer,是否必填:true),goodsSpecId(规格Id,string,是否必填:true),refundAmount(退款金额,售后类型为"部分退款"时必填,且最多支持2位小数,number,是否必填:true)
     * @param int $returnType 售后类型，1：退款，2：退货退款，3：换货，4：仅退款，5：部分退款
     * @param string $memo    备注信息
     * @return mixed
     */
    public function applyRefund(string $reason='',string $orderSn='',string $refundReasonId='',array $goodsList=[],int $returnType=1,string $memo='')
    {
        $params = [
            'reason' =>$reason,//	退费原因	string	是否必填:true
            //'applyWay'=>'',//	申请方式，1：主动申请；2：系统/客户代申请；默认为：1	integer	是否必填:false
            'orderSn' =>$orderSn,//	订单编号	string	是否必填:true
            'refundReasonId' =>$refundReasonId,//	退款原因ID, 请参考文档中心-订单售后申请原因说明文档	string	是否必填:true
            /*'goodsList' => [//	多商品规格id	array	是否必填:true
                'goodsQuantity' => '',//	商品数	integer	是否必填:true
                'goodsSpecId' => '',//	规格Id	string	是否必填:true
                'refundAmount' => '',//	退款金额,售后类型为"部分退款"时必填,且最多支持2位小数	number	true
            ],*/
            'goodsList' =>$goodsList,
            //'memo'=>'',//	问题描述	string	是否必填:false
            //'pictureurlList'=>'',//	退款上传图片URL集合	array	是否必填:false
            'returnType' =>$returnType,//	售后类型，1：退款，2：退货退款，3：换货，4：仅退款，5：部分退款	integer	是否必填:true
        ];
        if($memo)$params['memo'] = $memo;
        $resp =$this->postRequest(self::APIS['applyRefund'], $params, $this->header);
        return $resp;
    }


    /**
     * 退货/换货邮寄卖家
     * @param string $deliverySn 物流单号
     * @param string $deliveryCorpName 物流公司名称
     * @param string $returnSn 售后单号
     * @return mixed
     */
    public function sendReturnGoods(string $deliverySn='',string $deliveryCorpName='',string $returnSn='')
    {
        $params = [
            //'deliveryCorpSn'=>'',//	物流公司编码	string	是否必填:false
            'deliverySn' => '',//	物流单号	string	是否必填:true
            'deliveryCorpName' => '',//	物流公司名称	string	是否必填:true
            'returnSn' => '',//	售后单号	string	是否必填:true
        ];
        $resp =$this->postRequest(self::APIS['sendReturnGoods'], $params, $this->header);
        return $resp;
    }


    /**
     * 换货确认收货
     * @return void
     */
    public function receiptRefund()
    {
        $params = [
            //'returnSn'=>'',//售后单号	string	是否必填:false
        ];
        $resp =$this->postRequest(self::APIS['receiptRefund'], $params, $this->header);
        return $resp;
    }


    /**
     * 取消售后申请
     * @param string $returnSn 售后单号
     * @return mixed
     */
    public function cancelRefund(string $returnSn='')
    {
        $params = [
            'returnSn' => $returnSn,//售后单号	string	是否必填:true
        ];
        $resp =$this->postRequest(self::APIS['cancelRefund'], $params, $this->header);
        return $resp;
    }


    /**
     * 查询售后单详细信息
     * @param string $returnSn 售后单号
     * @return mixed
     */
    public function detail(string $returnSn='')
    {
        $params = [
            'returnSn' => $returnSn,//售后单号	string	是否必填:true
        ];
        $resp =$this->postRequest(self::APIS['detail'], $params, $this->header);
        return $resp;
    }


    /**
     * 生成签名
     * @return string
     */
    private function getSign(array $requestBody): string
    {
        foreach($requestBody as $k=>$item){
            if(is_array($requestBody[$k]))
                $requestBody[$k]=json_encode($item);
        }

        $params=[
            'appKey' => $this->appKey,
            'timeStamp'=>$this->timeStamp,
            'randomNumber'=>$this->randomNumber,
            'version'=>$this->version
        ];

        $params=array_merge($params,$requestBody);
        ksort($params);

        $sign =urldecode(http_build_query($params));
        $sign .='&'.$this->appSecret;
        //print_r($sign);
        $sign = unpack('c*', md5($sign, true));
        $sign = $this->encodeHex($sign);
        return $sign;
    }


    private function encodeHex(array $bytes): string
    {
        $lower = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
        $charArr = [];
        foreach ($bytes as $value) {
            $value = intval($value);
            $charArr[] = $lower[$this->uright(0xF0 & $value, 4)];
            $charArr[] = $lower[0x0F & $value];
        }
        return implode('', $charArr);
    }

    private function uright($left, $right)
    {
        $c = 2147483647 >> ($right - 1);
        return $c & ($left >> $right);
    }


    /**
     * 生成随机数
     * @return string
     */
    private function getRandomNo(): string
    {
        return rand(100000, 999999);
    }



    /**
     * 发送Post请求
     *
     * @param string $url 请求url
     * @param array/string $params 发送的参数
     */
    private function postRequest($url, $params =[],$header =[])
    {
        $this->timeStamp = time() * 1000;
        $this->randomNumber=$this->getRandomNo();
        $header[]='timeStamp:'.$this->timeStamp;
        $header[]='randomNumber:'.$this->randomNumber;
        $header[]='sign:'.$this->getSign($params);

        $params=$params?json_encode($params,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE):'{}';
        //print_r($params);exit;
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        $response = curl_exec($ch);

        if($error=curl_error($ch)){
            $text=date('Y-m-d H:i:s').PHP_EOL.'api='.$url.PHP_EOL.',body='.$params.PHP_EOL.',header='.json_encode($header).PHP_EOL.'error='.$error.PHP_EOL.PHP_EOL.PHP_EOL;
            file_put_contents('sceApi.log',$text,FILE_APPEND);
            return ['code'=>0,'msg'=>$error];
        }
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if($httpCode != 200){
            $text=date('Y-m-d H:i:s').PHP_EOL.'api='.$url.PHP_EOL.',body='.$params.PHP_EOL.',header='.json_encode($header).PHP_EOL.'response='.$response.PHP_EOL.PHP_EOL.PHP_EOL;
            file_put_contents('sceApi.log',$text,FILE_APPEND);
            return ['code'=>0,'msg'=>$response];
        }

        curl_close($ch);
        $resp=json_decode($response,true);
        $text=date('Y-m-d H:i:s').PHP_EOL.'api='.$url.PHP_EOL.',body='.$params.PHP_EOL.',header='.json_encode($header).PHP_EOL.'response='.$response.PHP_EOL.PHP_EOL.PHP_EOL;
        file_put_contents('sceApi.log',$text,FILE_APPEND);

        if(!$resp['success']) {
            return ['code'=>0,'msg'=>$resp['msg']];
        };
        return $resp['data'];
    }
}